﻿namespace PowerSong.BibleSupport
    {
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Contains methods for supporting Bible facilities in PowerSong.
    /// </summary>
    public sealed class BibleSupport
        {

        private BibleSupport()
            {
            }

        /// <summary>
        /// A book of the Bible.
        /// </summary>
        public enum BibleBook
            {
            Genesis,
            Exodus,
            Leviticus,
            Numbers,
            Deuteronomy,
            Joshua,
            Judges,
            Ruth,
            OneSamuel,
            TwoSamuel,
            OneKings,
            TwoKings,
            OneChronicles,
            TwoChronicles,
            Ezra,
            Nehemiah,
            Esther,
            Job,
            Psalm,
            Proverbs,
            Ecclesiastes,
            SongOfSolomon,
            Isaiah,
            Jeremiah,
            Lamentations,
            Ezekiel,
            Daniel,
            Hosea,
            Joel,
            Amos,
            Obadiah,
            Jonah,
            Micah,
            Nahum,
            Habakkuk,
            Zephaniah,
            Haggai,
            Zechariah,
            Malachi,
            Matthew,
            Mark,
            Luke,
            John,
            Acts,
            Romans,
            OneCorinthians,
            TwoCorinthians,
            Galatians,
            Ephesians,
            Philippians,
            Colossians,
            OneThessalonians,
            TwoThessalonians,
            OneTimothy,
            TwoTimothy,
            Titus,
            Philemon,
            Hebrews,
            James,
            OnePeter,
            TwoPeter,
            OneJohn,
            TwoJohn,
            ThreeJohn,
            Jude,
            Revelation
            }

        private static Dictionary<BibleBook, string> books = InitBooks();

        /// <summary>
        /// Gets the name of the book, given its related enumeration value.
        /// </summary>
        /// <param name="book">The book whose name to retrieve.</param>
        /// <returns>The name of the requested book.</returns>
        public static string GetBookName(BibleBook book)
            {
            return books[book];
            }

        private static Dictionary<BibleBook, string> InitBooks()
            {

            var result = new Dictionary<BibleBook, string>();
            result.Add(BibleBook.Genesis, "Genesis");
            result.Add(BibleBook.Exodus, "Exodus");
            result.Add(BibleBook.Leviticus, "Leviticus");
            result.Add(BibleBook.Numbers, "Numbers");
            result.Add(BibleBook.Deuteronomy, "Deuteronomy");
            result.Add(BibleBook.Joshua, "Joshua");
            result.Add(BibleBook.Judges, "Judges");
            result.Add(BibleBook.Ruth, "Ruth");
            result.Add(BibleBook.OneSamuel, "1 Samuel");
            result.Add(BibleBook.TwoSamuel, "2 Samuel");
            result.Add(BibleBook.OneKings, "1 Kings");
            result.Add(BibleBook.TwoKings, "2 Kings");
            result.Add(BibleBook.OneChronicles, "1 Chronicles");
            result.Add(BibleBook.TwoChronicles, "2 Chronicles");
            result.Add(BibleBook.Ezra, "Ezra");
            result.Add(BibleBook.Nehemiah, "Nehemiah");
            result.Add(BibleBook.Esther, "Esther");
            result.Add(BibleBook.Job, "Job");
            result.Add(BibleBook.Psalm, "Psalm");
            result.Add(BibleBook.Proverbs, "Proverbs");
            result.Add(BibleBook.Ecclesiastes, "Ecclesiastes");
            result.Add(BibleBook.SongOfSolomon, "Song of Solomon");
            result.Add(BibleBook.Isaiah, "Isaiah");
            result.Add(BibleBook.Jeremiah, "Jeremiah");
            result.Add(BibleBook.Lamentations, "Lamentations");
            result.Add(BibleBook.Ezekiel, "Ezekiel");
            result.Add(BibleBook.Daniel, "Daniel");
            result.Add(BibleBook.Hosea, "Hosea");
            result.Add(BibleBook.Joel, "Joel");
            result.Add(BibleBook.Amos, "Amos");
            result.Add(BibleBook.Obadiah, "Obadiah");
            result.Add(BibleBook.Jonah, "Jonah");
            result.Add(BibleBook.Micah, "Micah");
            result.Add(BibleBook.Nahum, "Nahum");
            result.Add(BibleBook.Habakkuk, "Habakkuk");
            result.Add(BibleBook.Zephaniah, "Zephaniah");
            result.Add(BibleBook.Haggai, "Haggai");
            result.Add(BibleBook.Zechariah, "Zechariah");
            result.Add(BibleBook.Malachi, "Malachi");
            result.Add(BibleBook.Matthew, "Matthew");
            result.Add(BibleBook.Mark, "Mark");
            result.Add(BibleBook.Luke, "Luke");
            result.Add(BibleBook.John, "John");
            result.Add(BibleBook.Acts, "Acts");
            result.Add(BibleBook.Romans, "Romans");
            result.Add(BibleBook.OneCorinthians, "1 Corinthians");
            result.Add(BibleBook.TwoCorinthians, "2 Corinthians");
            result.Add(BibleBook.Galatians, "Galatians");
            result.Add(BibleBook.Ephesians, "Ephesians");
            result.Add(BibleBook.Philippians, "Philippians");
            result.Add(BibleBook.Colossians, "Colossians");
            result.Add(BibleBook.OneThessalonians, "1 Thessalonians");
            result.Add(BibleBook.TwoThessalonians, "2 Thessalonians");
            result.Add(BibleBook.OneTimothy, "1 Timothy");
            result.Add(BibleBook.TwoTimothy, "2 Timothy");
            result.Add(BibleBook.Titus, "Titus");
            result.Add(BibleBook.Philemon, "Philemon");
            result.Add(BibleBook.Hebrews, "Hebrews");
            result.Add(BibleBook.James, "James");
            result.Add(BibleBook.OnePeter, "1 Peter");
            result.Add(BibleBook.TwoPeter, "2 Peter");
            result.Add(BibleBook.OneJohn, "1 John");
            result.Add(BibleBook.TwoJohn, "2 John");
            result.Add(BibleBook.ThreeJohn, "3 John");
            result.Add(BibleBook.Jude, "Jude");
            result.Add(BibleBook.Revelation, "Revelation");
            return result;

            }

        /// <summary>
        /// Determines the references.
        /// </summary>
        /// <param name="term">The term.</param>
        /// <returns></returns>
        public static List<BibleReference> DetermineReferences(string term)
            {

            if (string.IsNullOrEmpty(term)) return new List<BibleReference>();

            // Extract the book name
            int chapterStartPos = -1;
            bool foundBookName = false;
            do
                {
                chapterStartPos += 1;
                if (chapterStartPos < term.Length && !IsNumeric(term[chapterStartPos])) foundBookName = true;
                } 
            while (!(chapterStartPos >= term.Length || (foundBookName && IsNumeric(term[chapterStartPos]))));

            // Extract the chapter/verse component
            var bookName = term.Substring(0, chapterStartPos);
            var chapterVerse = string.Empty;
            if (chapterStartPos < term.Length) chapterVerse = term.Substring(chapterStartPos);

            // Try to find matching books
            var matches = GetBibleBooks(bookName);

            // Put the most likely book at the top of the list
            if (matches.Count > 0)
                {

                // Determine the best match
                var bestMatch = matches[0];
                foreach (var match in matches)
                    {
                    if (BibleSupport.GetBookName(match).ToUpper().IndexOf(bookName.ToUpper()) == 0) bestMatch = match;
                    }

                // Put the best match at the top
                if (bestMatch != matches[0])
                    {
                    // TODO: Is temp needed?
                    BibleSupport.BibleBook temp = matches[0];
                    matches[matches.IndexOf(bestMatch)] = matches[0];
                    matches[0] = bestMatch;
                    }

                }

            // Determine chapter and verse referenced
            int splitPoint = 0;
            do
                {
                splitPoint += 1;
                }
            while (splitPoint < chapterVerse.Length && IsNumeric(chapterVerse[splitPoint]));
            int chapter = -1;
            int verse = -1;

            if (splitPoint < chapterVerse.Length)
                {

                // Split point found
                var chapterPart = chapterVerse.Substring(0, splitPoint);
                var afterChapterPart = chapterVerse.Substring(splitPoint + 1);
                var versePart = string.Empty;
                foreach (char c in afterChapterPart)
                    {
                    if (c >= '0' && c <= '9') versePart += c;
                    }
                chapter = Convert.ToInt32(chapterPart);
                if (!string.IsNullOrEmpty(versePart)) verse = Convert.ToInt32(versePart);

                }
            else
                {

                // No split point found
                if (!string.IsNullOrEmpty(chapterVerse)) chapter = Convert.ToInt32(chapterVerse);

                }

            // Return a list of possible results
            var result = new List<BibleReference>();
            foreach (var match in matches)
                {
                result.Add(new BibleReference(match, chapter, verse));
                }
            return result;

            }

        public static BibleReference DetermineSingleReference(string term)
            {

            var references = DetermineReferences(term);
            if (references.Count == 0)
                {
                return null;
                }
            else
                {
                return references[0];
                }

            }

        private static List<BibleBook> GetBibleBooks(string bookName)
            {

            bookName = bookName.Replace(" ", "").ToUpper();
            var matches = new List<BibleBook>();

            // Check that a search is necessary
            if (string.IsNullOrEmpty(bookName)) return matches;

            // Search all book names
            foreach (BibleBook value in Enum.GetValues(typeof(BibleBook)))
                {
                var testBookName = books[value].Replace(" ", "").ToUpper();
                if (bookName[0] == testBookName[0] && IsStringMatch(bookName, testBookName))
                    {
                    matches.Add(value);
                    }
                }
            return matches;

            }

        private static bool IsNumeric(char c)
            {
            if (c >= '0' && c <= '9') return true;
            return false;
            }

        private static bool IsStringMatch(string source, string target)
            {

            int sourcePos = 0;
            int targetPos = 0;
            while (targetPos < target.Length)
                {
                if (sourcePos < source.Length && source[sourcePos] == target[targetPos])
                    {
                    sourcePos += 1;
                    }
                targetPos += 1;
                }
            if (sourcePos >= source.Length) return true;
            return false;

            }

        }

    }
